#include "monitor.h"
#include "temu.h"
#include "monitor/expr.h"
#include "monitor/watchpoint.h"

#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>

void cpu_exec(uint32_t);

void display_reg();

/* We use the `readline' library to provide more flexibility to read from stdin. */
char* rl_gets() {
	static char *line_read = NULL;

	if (line_read) {
		free(line_read);
		line_read = NULL;
	}

	line_read = readline("(temu) ");

	if (line_read && *line_read) {
		add_history(line_read);
	}

	return line_read;
}

static int cmd_c(char *args) {
	cpu_exec(-1);
	return 0;
}

static int cmd_q(char *args) {
	return -1;
}

static int cmd_help(char *args);

static int cmd_si(char* args);

static int cmd_info(char* args);

static int cmd_x(char* args);

static int cmd_p(char* args);

static int cmd_w(char* args);
static int cmd_d(char* args);

static struct {
	char *name;
	char *description;
	int (*handler) (char *);
} cmd_table [] = {
	{ "help", "Display informations about all supported commands", cmd_help },
	{ "c", "Continue the execution of the program", cmd_c },
	{ "q", "Exit TEMU", cmd_q },
	{ "si", "exec commands  step by step",cmd_si},
	{ "info", "print the state of register", cmd_info},
	{ "x", "scan the 4*N bytes' data from expr", cmd_x},
	{"p", "calculate the outcome of expression", cmd_p},
	{"w","set watchpoint",cmd_w},
	{"d", "delete watchpoint", cmd_d}

	/* TODO: Add more commands */

};

#define NR_CMD (sizeof(cmd_table) / sizeof(cmd_table[0]))

static int cmd_help(char *args) {
	/* extract the first argument */
	char *arg = strtok(NULL, " ");
	int i;

	if(arg == NULL) {
		/* no argument given */
		for(i = 0; i < NR_CMD; i ++) {
			printf("%s - %s\n", cmd_table[i].name, cmd_table[i].description);
		}
	}
	else {
		for(i = 0; i < NR_CMD; i ++) {
			if(strcmp(arg, cmd_table[i].name) == 0) {
				printf("%s - %s\n", cmd_table[i].name, cmd_table[i].description);
				return 0;
			}
		}
		printf("help's Unknown command '%s'\n", arg);
	}
	return 0;
}

static int cmd_si(char* args){
	char* arg = strtok(NULL," ");
	int num=0;
	if(arg==NULL){
		//No argument!
		//default exec 1 command
		cpu_exec(1); 
		return 0;
	}else {
		num=atoi(arg);
		if(num<0){
			printf("input wrong parameter\n");//Because exec's parameter is unsigned
			//if give a negative number, it will become veryyyyyy bigggggg!
			return -1;//abnormal
		}
		cpu_exec(num);
		return 0;
	}
}

static int cmd_info(char* args){
	char* arg=strtok(NULL," ");
	if(arg==NULL) {
		printf("Lack arguments, Please type help for more information\n");
		return -1;
	}
	if(strcmp(arg,"r")==0){
		display_reg();
		return 0;
	}else if( strcmp(arg,"w")==0){
		info_wp();
		return 1;
		return 0;
	}else {
		printf("Input wrong arguments\n");
		return -1;
	}
	return 0;
}

static int cmd_x(char* args){
	char* arg1 = strtok(NULL, " ");
	char* arg2 = strtok(NULL, " ");
	uint32_t address; int range =0;
	sscanf(arg1,"%d", &range); 	sscanf(arg2,"%x",&address);

	int i=0; int data;
	// uint8_t buf[4];
	for (i=0;i<range;i++){
		printf("0x%08x: ",address+i*4);
		data=mem_read(address+i*4, 4);
		int j=0; 
		for(j=0;j<4;j++){
			printf("0x%02x ", data&0xff);
			//  buf[3-j]=data&0xff;
			data=data>>8; 
		}
		// for(j=0;j<4;j++){
		// 	printf("0x%02x ", buf[j]);
		// }
		printf("\n");
	}
	return 0;
}

static int cmd_p(char* args){
	uint32_t outtcome;
	bool success;
	outtcome=expr(args,&success);
	if(success)
	printf("Hex:0x%x DEC:%d\n",outtcome,outtcome);
	else {assert(0);}
	return 0;
}

static int cmd_w(char* args){
	WP* p;
	bool success;
	p=new_wp();
	printf("Watchpoint %d: %s set",p->NO,args);
	p->val=expr(args,&success);
	if(!success){Assert(1,"Wrong expr format\n");}
	strcpy(p->expr,args);
	printf("Now value is %d\n",p->val);
	return 0;
}

static int cmd_d(char*args)
{
	char*arg=strtok(NULL," ");
	int num;
	sscanf(arg,"%d",&num);
	delete_wp(num);
	return 0;
}

void ui_mainloop() {
	while(1) {
		char *str = rl_gets();
		char *str_end = str + strlen(str);

		/* extract the first token as the command */
		char *cmd = strtok(str, " ");
		if(cmd == NULL) { continue; }

		/* treat the remaining string as the arguments,
		 * which may need further parsing
		 */
		char *args = cmd + strlen(cmd) + 1;
		if(args >= str_end) {
			args = NULL;
		}

		int i;
		for(i = 0; i < NR_CMD; i ++) {
			if(strcmp(cmd, cmd_table[i].name) == 0) {
				if(cmd_table[i].handler(args) < 0) { return; }
				break;
			}
		}

		if(i == NR_CMD) { printf("UI's Unknown command '%s'\n", cmd); }
	}
}
